package com.haier.os.security.core.validate.code;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import java.io.IOException;
import java.util.Map;

/**
 * 通用验证码处理器
 * @param <T>
 */
@Slf4j
public abstract class AbstractValidateCodeProcessor<T extends ValidateCode> implements ValidateCodeProcessor {
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    /**
     * 收集系统中所有的 {@link ValidateCodeGenerator} 接口的实现。
     */
    @Autowired
    private Map<String, ValidateCodeGenerator> validateCodeGenerators;
    @Override
    public T createValidateCode(ServletWebRequest request) throws IOException, ServletRequestBindingException {
        //1.生成验证码
        T validateCode = generator(request);
        //2.将验证码保存到session中
        save(request, validateCode);
        //3.返回验证码
        send(request, validateCode);

        return validateCode;
    }

    /**
     * 保存验证码到session
     * @param request
     * @param validateCode
     */
    private void save(ServletWebRequest request, T validateCode) {
        sessionStrategy.setAttribute(request, SESSION_KEY_PREFIX + StringUtils.upperCase(getValidateCodeType(request).toString()), validateCode);
    }

    /**
     * 根据请求的url获取验证码的类型
     * @param request
     * @return
     */
    private ValidateCodeType getValidateCodeType(ServletWebRequest request) {
//        return StringUtils.substringAfter(request.getRequest().getRequestURI(), "/code/");
        String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor");
        return ValidateCodeType.valueOf(type.toUpperCase());
    }


    /**
     * 发送验证码
     * @param request
     * @param validateCode
     */
    protected abstract void send(ServletWebRequest request, T validateCode) throws IOException, ServletRequestBindingException;

    private T generator(ServletWebRequest request) {
        ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(getValidateCodeType(request).toString().toLowerCase() + "CodeGenerator");
        if (null == validateCodeGenerator) {
            log.error("validateCodeGenerator is null");
            return null;
        }
        return (T) validateCodeGenerator.generator(request);
    }

    @Override
    public void validate(ServletWebRequest request) {
        ValidateCodeType type = getValidateCodeType(request);
        String sessionKey = SESSION_KEY_PREFIX + type.toString();
        ValidateCode codeInSession = (ValidateCode) sessionStrategy.getAttribute(request, sessionKey);
        String codeInRequest = null;
        try {
            codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), type.getParamNameOnValidate());
        } catch (ServletRequestBindingException e) {
            throw new ValidateCodeException("获取验证码的值失败");
        }

        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("验证码不能为空");
        }

        if (null == codeInSession) {
            throw new ValidateCodeException("验证码不存在");
        }

        if (codeInSession.isExpired()) {
            sessionStrategy.removeAttribute(request, sessionKey);
            throw new ValidateCodeException("验证码已过期");
        }

        if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
            throw new ValidateCodeException("验证码不匹配");
        }

        sessionStrategy.removeAttribute(request, sessionKey);
    }
}
